/*  (c) 2008 Benjamin Schroeter
 *  This file is part of JaDAL - Just another DSL-Tools Addon Library
 *  and licensed under the New BSD License
 *  For more information please visit http://www.codeplex.com/JaDAL */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Xml.Linq;

namespace BenjaminSchroeter.Dsl.DynamicProperties
{
    public class DynamicPropertyCollectionTypeConverter : ExpandableObjectConverter
    {
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string))
                return true;

            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                // this is more or less a workaround:
                // when a DynamicPropertyCollection is shown in the PropertiyGrid there should be shown nothing in the root element
                // the TypeComverter to string is only used to serialize the data
                if (context != null && context.ToString().Contains("PropertyGrid")) 
                    return "";

                DynamicPropertyCollection properties = (DynamicPropertyCollection)value;

                StringBuilder sb = new StringBuilder();
                sb.Append("\n");
                int i = 1;
                foreach (DynamicProperty property in properties)
                {
                    // I'm using the XML functions here so I need not think about escaping chars and so on
                    XAttribute attr = new XAttribute("name_" + i, property.Name);
                    sb.Append(attr.ToString());
                    sb.Append(" ");
                    attr = new XAttribute("type_" + i, property.Type.ToString());
                    sb.Append(attr.ToString());
                    sb.Append(" ");
                    attr = new XAttribute("value_" + i, ConvertValueToString(property.Value));
                    sb.Append(attr.ToString());
                    sb.Append(" ");

                    sb.Append("\n");
                    
                    i++;
                }

                return sb.ToString();
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if ( sourceType == typeof(string))
                return true;
            
            return base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string)
            {
                XElement container = XElement.Parse("<container " +  (string) value + " />");

                Dictionary<int, DynamicProperty> properties = new Dictionary<int, DynamicProperty>();
                
                foreach (XAttribute attribute in container.Attributes())
                {
                    string name = attribute.Name.LocalName;
                    string[] nameparts = name.Split('_');
                    if (nameparts.Length < 2)
                        continue;
                    
                    int number = int.Parse(nameparts[1]);

                    // find or create the DynamicProperty with this number
                    DynamicProperty p;                     
                    if ( !properties.TryGetValue(number, out p) )
                    {
                        p = new DynamicProperty();
                        properties.Add(number, p);
                    }

                    if (nameparts[0] == "name")
                        p.Name = attribute.Value;
                    else if (nameparts[0] == "type")
                        p.Type = Type.GetType(attribute.Value);
                    else if (nameparts[0] == "value")
                        p.Value = ConvertValueFromString(attribute.Value, p.Type);
                }

                DynamicPropertyCollection output = new DynamicPropertyCollection();
                foreach (KeyValuePair<int, DynamicProperty> pair in properties)
                    output.AddProperty(pair.Value.Name,pair.Value.Type,pair.Value.Value);
    
                return output;
            }

            return base.ConvertFrom(context, culture, value);
        }

        private static string ConvertValueToString(object value)
        {
            if (value == null)
                return "";

            TypeConverter converter = TypeDescriptor.GetConverter(value);

            if (converter.CanConvertTo(typeof(string)))
                return converter.ConvertToInvariantString(value);

            return "";
        }

        private static object ConvertValueFromString(string str, Type type)
        {
            TypeConverter converter = TypeDescriptor.GetConverter(type);

            try
            {
                if (converter.CanConvertFrom(typeof(string)))
                    return converter.ConvertFromInvariantString(str);
            }
            catch {}

            return null;
        }
    }
}